///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_IMAGE_RASTERIZER_CLINEINTERPOLATOR_H_
#define _BUOLA_IMAGE_RASTERIZER_CLINEINTERPOLATOR_H_

#include <buola/image.h>

namespace buola { namespace img {

#if 0
    //===================================================dda_line_interpolator
    template<int FractionShift, int YShift=0> class dda_line_interpolator
    {
    public:
        //--------------------------------------------------------------------
        dda_line_interpolator() {}

        //--------------------------------------------------------------------
        dda_line_interpolator(int y1, int y2, unsigned count) :
            m_y(y1),
            m_inc(((y2 - y1) << FractionShift) / int(count)),
            m_dy(0)
        {
        }

        //--------------------------------------------------------------------
        void operator ++ ()
        {
            m_dy += m_inc;
        }

        //--------------------------------------------------------------------
        void operator -- ()
        {
            m_dy -= m_inc;
        }

        //--------------------------------------------------------------------
        void operator += (unsigned n)
        {
            m_dy += m_inc * n;
        }

        //--------------------------------------------------------------------
        void operator -= (unsigned n)
        {
            m_dy -= m_inc * n;
        }


        //--------------------------------------------------------------------
        int y()  const { return m_y + (m_dy >> (FractionShift-YShift)); }
        int dy() const { return m_dy; }


    private:
        int m_y;
        int m_inc;
        int m_dy;
    };

#endif

class CDDALineInterpolator
{
public:
    CDDALineInterpolator() {}

    CDDALineInterpolator(int pY1,int pY2,int pCount)
        :   mCount(pCount>0?-pCount:-1)
        ,   mInt((pY2-pY1)/-mCount)
        ,   mRem((pY2-pY1)%-mCount)
        ,   mMod(mRem)
        ,   mY(pY1)
    {
        if(mMod<=0)
        {
            mMod-=mCount;
            mRem-=mCount;
            mInt--;
        }
        mMod+=mCount;
    }

    void operator++()
    {
        mMod+=mRem;
        mY+=mInt;
        if(mMod>0)
        {
            mMod+=mCount;
            mY++;
        }
    }

    void operator--()
    {
        mMod-=mRem;
        mY-=mInt;
        if(mMod<mCount)
        {
            mMod-=mCount;
            mY--;
        }
    }

    int Y() const { return mY; }

private:
    int mCount; //actually, -mCount
    int mInt;
    int mRem;
    int mMod;
    int mY;
};

template<typename tRenderer>
void render_line(tRenderer &pR,const CPoint_i &p1,const CPoint_i &p2,const typename tRenderer::TPixel &pPixel,
                 bool pLast=false)
{
    using std::swap;
    
    CPoint_i l1=p1;
    CPoint_i l2=p2;

    int lDX=abs(l2.x-l1.x);
    int lDY=abs(l2.y-l1.y);

    if(lDY>lDX) //vertical line
    {
        if(l1.y>l2.y) swap(l1,l2);
        if(pLast) ++lDY;
            
        CDDALineInterpolator lI(l1.x,l2.x,lDY);

        while(lDY--)
        {
            pR(lI.Y(),l1.y)=pPixel;
            ++lI;
            ++l1.y;
        }
    }
    else
    {
        if(l1.x>l2.x) swap(l1,l2);
        if(pLast) ++lDX;

        CDDALineInterpolator lI(l1.y,l2.y,lDX);

        while(lDX--)
        {
            pR(l1.x,lI.Y())=pPixel;
            ++lI;
            ++l1.x;
        }
    }
}

template<typename tRenderer>
void render_line_aa(tRenderer &pR,const CPoint_i &p1,const CPoint_i &p2,const typename tRenderer::TPixel &pPixel,
                 bool pLast=false)
{
    using std::swap;

    CPoint_i l1=p1;
    CPoint_i l2=p2;

    int lDX=abs(l2.x-l1.x);
    int lDY=abs(l2.y-l1.y);

    if(lDY>lDX) //vertical line
    {
        if(l1.y>l2.y) swap(l1,l2);
        l1.x*=256;
        l2.x*=256;
        if(pLast) ++lDY;

        CDDALineInterpolator lI(l1.x,l2.x,lDY);

        while(lDY--)
        {
            int x=lI.Y()+128;
            int lInt=x>>8;
            int lFrac=x&0xff;
            
            pR(lInt,l1.y).Blend(pPixel,lFrac);
            pR(lInt-1,l1.y).Blend(pPixel,255-lFrac);
            ++lI;
            ++l1.y;
        }
    }
    else
    {
        if(l1.x>l2.x) swap(l1,l2);
        l1.y*=256;
        l2.y*=256;
        if(pLast) ++lDX;

        CDDALineInterpolator lI(l1.y,l2.y,lDX);

        while(lDX--)
        {
            int y=lI.Y()+128;
            int lInt=y>>8;
            int lFrac=y&0xff;

            pR(l1.x,lInt).Blend(pPixel,lFrac);
            pR(l1.x,lInt-1).Blend(pPixel,255-lFrac);
            ++lI;
            ++l1.x;
        }
    }
}

#if 0
class CBresenhamLineInterpolator
{
public:
    enum subpixel_scale_e
    {
        subpixel_shift = 8,
        subpixel_scale = 1 << subpixel_shift,
        subpixel_mask  = subpixel_scale - 1
    };

    //--------------------------------------------------------------------
    static int line_lr(int v) { return v >> subpixel_shift; }

    //--------------------------------------------------------------------
    CBresenhamLineInterpolator(int x1, int y1, int x2, int y2) :
        m_x1_lr(line_lr(x1)),
        m_y1_lr(line_lr(y1)),
        m_x2_lr(line_lr(x2)),
        m_y2_lr(line_lr(y2)),
        m_ver(abs(m_x2_lr - m_x1_lr) < abs(m_y2_lr - m_y1_lr)),
        m_len(m_ver ? abs(m_y2_lr - m_y1_lr) :
                        abs(m_x2_lr - m_x1_lr)),
        m_inc(m_ver ? ((y2 > y1) ? 1 : -1) : ((x2 > x1) ? 1 : -1)),
        m_interpolator(m_ver ? x1 : y1,
                        m_ver ? x2 : y2,
                        m_len)
    {
    }

    //--------------------------------------------------------------------
    bool     is_ver() const { return m_ver; }
    unsigned len()    const { return m_len; }
    int      inc()    const { return m_inc; }

    //--------------------------------------------------------------------
    void hstep()
    {
        ++m_interpolator;
        m_x1_lr += m_inc;
    }

    //--------------------------------------------------------------------
    void vstep()
    {
        ++m_interpolator;
        m_y1_lr += m_inc;
    }

    //--------------------------------------------------------------------
    int x1() const { return m_x1_lr; }
    int y1() const { return m_y1_lr; }
    int x2() const { return line_lr(m_interpolator.y()); }
    int y2() const { return line_lr(m_interpolator.y()); }
    int x2_hr() const { return m_interpolator.y(); }
    int y2_hr() const { return m_interpolator.y(); }

private:
    int                    m_x1_lr;
    int                    m_y1_lr;
    int                    m_x2_lr;
    int                    m_y2_lr;
    bool                   m_ver;
    unsigned               m_len;
    int                    m_inc;
    dda2_line_interpolator m_interpolator;

};
#endif

/*namespace image*/ } /*namespace buola*/ }

#endif
